<!DOCTYPE html>
<html>
    <head>
        <title>PushSubscriptionOptions should be correctly reflected on PushSubscription objects</title>
        <link rel="manifest" href="resources/push_manifest.json">
        <script src="resources/push-constants.js"></script>
        <script src="../resources/testharness.js"></script>
        <script src="../resources/testharnessreport.js"></script>
        <script src="../serviceworker/resources/test-helpers.js"></script>
    </head>
    <body>
        <script>
            // When running this test manually, grant permission when prompted.
            if (window.testRunner)
                testRunner.setPermission('push-messaging', 'granted', location.origin, location.origin);

            function assert_options_equals(actual_subscription, expected_options, description) {

                // PushSubscription should have:
                // [SameObject] readonly attribute PushSubscriptionOptions options;
                assert_true(actual_subscription instanceof PushSubscription,
                            description + ": instanceof PushSubscription");
                assert_inherits(actual_subscription, 'options', description);
                assert_readonly(actual_subscription, 'options', description);
                assert_equals(actual_subscription.options, actual_subscription.options,
                              description + ": options [SameObject]");
                var actual_options = actual_subscription.options;

                // PushSubscriptionOptions should have:
                // readonly attribute boolean userVisibleOnly;
                // [SameObject] readonly attribute ArrayBuffer? applicationServerKey;
                assert_true(actual_options instanceof PushSubscriptionOptions,
                            description + ": options instanceof PushSubscriptionOptions");
                assert_inherits(actual_options, 'userVisibleOnly', description);
                assert_inherits(actual_options, 'applicationServerKey', description);
                assert_readonly(actual_options, 'userVisibleOnly', description);
                assert_readonly(actual_options, 'applicationServerKey', description);
                assert_equals(actual_options.applicationServerKey, actual_options.applicationServerKey,
                              description + ": applicationServerKey [SameObject]");

                // PushSubscriptionOptions should match expected_options.
                assert_equals(actual_options.userVisibleOnly, expected_options.userVisibleOnly,
                              description + ": userVisibleOnly");
                if (!expected_options.applicationServerKey) {
                    assert_equals(actual_options.applicationServerKey, null,
                                  description + ": applicationServerKey");
                } else {
                    assert_true(actual_options.applicationServerKey instanceof ArrayBuffer,
                                description + ": applicationServerKey instanceof window.ArrayBuffer");
                    var actual_bytes = new Uint8Array(actual_options.applicationServerKey);
                    var expected_bytes = new Uint8Array(expected_options.applicationServerKey);
                    assert_equals(actual_bytes.byteLength, expected_bytes.byteLength,
                                  description + ": applicationServerKey byteLength");
                    for (var i = 0; i < expected_bytes.byteLength; i++) {
                        assert_equals(actual_bytes[i], expected_bytes[i],
                                      description + ": applicationServerKey[" + i + "]");
                    }

                    // The applicationServerKey is actually mutable (https://github.com/w3c/push-api/issues/198).
                    new Uint8Array(actual_options.applicationServerKey)[0] = 0x7F;
                    assert_equals(new Uint8Array(actual_subscription.options.applicationServerKey)[0], 0x7F,
                                  description + ": applicationServerKey mutable");
                }
            }

            const VAPID_OPTIONS = {
                userVisibleOnly: true,
                applicationServerKey: new Uint8Array(PUBLIC_KEY)
            };
            const SENDER_ID_OPTIONS = {
                userVisibleOnly: true,
                // This is equal to the gcm_sender_id in push_manifest.json
                applicationServerKey: new TextEncoder().encode('1234567890')
            };
            const MANIFEST_OPTIONS = {
                userVisibleOnly: true
            };

            // This test verifies that PushSubscriptionOptions is correctly
            // reflected on PushSubscription objects.
            promise_test(test => {
                var workerScript = 'resources/instrumentation-service-worker.js';
                var workerScope = 'resources/scope/' + location.pathname;
                var pushManager;

                return service_worker_unregister_and_register(test, workerScript, workerScope)
                    .then(serviceWorkerRegistration => {
                        pushManager = serviceWorkerRegistration.pushManager;
                        return wait_for_state(test, serviceWorkerRegistration.installing, 'activated');
                    }).then(() => {

                        // 1. Test subscription with SENDER_ID_OPTIONS.
                        // -------------------------------------------.
                        return pushManager.subscribe(SENDER_ID_OPTIONS);
                    }).then(pushSubscription => {
                        assert_options_equals(pushSubscription, SENDER_ID_OPTIONS,
                                              "subscribe with SENDER_ID_OPTIONS");
                        return pushManager.getSubscription();
                    }).then(pushSubscription => {
                        assert_options_equals(pushSubscription, SENDER_ID_OPTIONS,
                                              "getSubscription with SENDER_ID_OPTIONS");
                        return pushSubscription.unsubscribe();
                    }).then(unsubscribed => {
                        assert_true(unsubscribed, "unsubscribed (with SENDER_ID_OPTIONS)");

                        // 2. Test subscription with VAPID_OPTIONS.
                        // ----------------------------------------
                        return pushManager.subscribe(VAPID_OPTIONS);
                    }).then(pushSubscription => {
                        assert_options_equals(pushSubscription, VAPID_OPTIONS,
                                              "subscribe with VAPID_OPTIONS");
                        return pushManager.getSubscription();
                    }).then(pushSubscription => {
                        assert_options_equals(pushSubscription, VAPID_OPTIONS,
                                              "getSubscription with VAPID_OPTIONS");
                        return pushSubscription.unsubscribe();
                    }).then(unsubscribed => {
                        assert_true(unsubscribed, "unsubscribed (with VAPID_OPTIONS)");

                        // 3. Test subscription with MANIFEST_OPTIONS.
                        // -------------------------------------------
                        return pushManager.subscribe(MANIFEST_OPTIONS);
                    }).then(pushSubscription => {
                        // Since MANIFEST_OPTIONS passed to subscribe has no
                        // applicationServerKey, the sender ID from the manifest
                        // should be reflected in the PushSubscriptionOptions.
                        assert_options_equals(pushSubscription, SENDER_ID_OPTIONS,
                                              "subscribe with MANIFEST_OPTIONS");
                        return pushManager.getSubscription();
                    }).then(pushSubscription => {
                        assert_options_equals(pushSubscription, SENDER_ID_OPTIONS,
                                              "getSubscription with MANIFEST_OPTIONS");
                    });

            }, "PushSubscriptionOptions should be correctly reflected on PushSubscription objects");
        </script>
    </body>
</html>
